﻿using System;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Net.Http;
using System.Data;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using RafikiSIS_Site.Models;
using System.Collections.Generic;
using System.Collections;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RafikiSIS_Site.Libraries;

namespace RafikiSIS_Site.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        private ApplicationUserManager _userManager;

        #region Constructors

        public AccountController()
        {
        }

        public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
        {
            UserManager = userManager;
            SignInManager = signInManager;
        }

        public ApplicationUserManager UserManager
        {
            get
            {
                return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
            private set
            {
                _userManager = value;
            }
        }

        private ApplicationSignInManager _signInManager;

        public ApplicationSignInManager SignInManager
        {
            get
            {
                return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
            }
            private set { _signInManager = value; }
        }
        #endregion

        #region Login
        //
        // GET: /Account/Login
        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;

            if(Request.UserHostAddress.Length > 6 || Request.UserHostAddress == "::1")
            {
                if (Request.UserHostAddress == "::1" ||
                    Request.UserHostAddress.Substring(0, 6) == System.Configuration.ConfigurationManager.AppSettings["InternalIPs"])
                {
                    // If the user is on our internal network, 
                    return RedirectToAction("ExternalLogin", new { ReturnUrl = returnUrl });
                }
            }
            return View();
        }

        //
        // GET: /Account/LoginNR
        [AllowAnonymous]
        public ActionResult LoginNR(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;

            return View();
        }

        //
        // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            ApplicationUser userobj;
            bool userExpired = false;

            if (!ModelState.IsValid)
            {
                return View(model);
            }

            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, change to shouldLockout: true
            SignInStatus result;
            // if the username contains an @ symbol, do a normal sign-on
            // otherwise, try authenticating the credentials via the domain
            if (model.Email.Contains("@"))
            {
                // Update the permissions here if the user already exists
                var permissionsResult = await UpdatePermissions(model.Email);
                // if the permissions change did not succeed, note errors and return to login screen
                if(!permissionsResult.Succeeded)
                {
                    ModelState.AddModelError("", permissionsResult.Errors.First().ToString());
                    return View(model);
                }
                result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
            }
            else
            {
                string LogonName;
                string UserSID = "";

                // If the domain name is at the beginning of the username, strip it off before looking for the user object.
                if (model.Email.IndexOf("\\") == -1)
                {
                    LogonName = model.Email;
                }
                else
                {
                    LogonName = model.Email.Split('\\')[1];
                }
                // validate password and reject if password is incorrect
                ADFuncs MyADQuery = new ADFuncs();
                if (!MyADQuery.ValidateCredentials(LogonName,model.Password))
                {
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
                }
                else 
                {
                    UserSID = MyADQuery.GetUser(LogonName).Sid.ToString();
                    userExpired = MyADQuery.IsUserPasswordExpired(LogonName);
                }

                // Find the user object and create if it doesn't exist
                userobj = await UserManager.FindByNameAsync(LogonName);
                if(userobj == null)
                {
                    var user = new ApplicationUser { UserName = LogonName, Email = LogonName + "@rva.org" };
                    var result2 = await UserManager.CreateAsync(user);
                    if (result2.Succeeded)
                    {
                        result2 = await UserManager.AddLoginAsync(user.Id, new UserLoginInfo("Windows",UserSID));
                        if (!result2.Succeeded)
                        {
                            ModelState.AddModelError("", result2.Errors.First().ToString());
                            return View(model);
                        }
                    }
                    else
                    {
                        ModelState.AddModelError("", result2.Errors.First().ToString());
                        return View(model);
                    }
                    // now that the user has been created, find the user object
                    userobj = await UserManager.FindByNameAsync(LogonName);
                }

                // Update the permissions after making sure the Windows user has been created.
                var permissionsResult = await UpdatePermissions(model.Email);
                // if the permissions change did not succeed, note errors and return to login screen
                if(!permissionsResult.Succeeded)
                {
                    ModelState.AddModelError("", permissionsResult.Errors.First().ToString());
                    return View(model);
                }
                await SignInManager.SignInAsync(userobj, isPersistent: false, rememberBrowser: false);
                result = SignInStatus.Success;
            }

            // At this point the user has a sign-in status.  If they successfully signed in, have an @ in their login
            // and EmailConfirmed is not true, then they will be redirected to a password expiration window.

            switch (result)
            {
                case SignInStatus.Success:
                    userobj = await UserManager.FindByNameAsync(model.Email);
                    if(model.Email.Contains('@') && !userobj.EmailConfirmed)
                    {
                        return RedirectToAction("ChangePassword", "Manage", new { ForceReset = true });
                    }
                    foreach(var tempLogin in userobj.Logins)
                    {
                        if(tempLogin.LoginProvider == "Windows" && userExpired)
                        {
                            return RedirectToAction("ChangePassword", "Manage", new { ForceReset = true });
                        }
                    }
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
            }
        }

        #endregion

        #region VerifyCode
        //
        // GET: /Account/VerifyCode
        [AllowAnonymous]
        public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
        {
            // Require that the user has already logged in via username/password or external login
            if (!await SignInManager.HasBeenVerifiedAsync())
            {
                return View("Error");
            }
            var user = await UserManager.FindByIdAsync(await SignInManager.GetVerifiedUserIdAsync());
            if (user != null)
            {
                var code = await UserManager.GenerateTwoFactorTokenAsync(user.Id, provider);
            }
            return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
        }

        //
        // POST: /Account/VerifyCode
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            // The following code protects for brute force attacks against the two factor codes. 
            // If a user enters incorrect codes for a specified amount of time then the user account 
            // will be locked out for a specified amount of time. 
            // You can configure the account lockout settings in IdentityConfig
            var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent:  model.RememberMe, rememberBrowser: model.RememberBrowser);
            switch (result)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(model.ReturnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid code.");
                    return View(model);
            }
        }

        #endregion

        #region Register
        //
        // GET: /Account/RegisterUser
        [Authorize(Users = "tchase")]
        public ActionResult RegisterUser()
        {
            return View();
        }

        //
        // POST: /Account/RegisterUser
        [HttpPost]
        [Authorize(Users = "tchase")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> RegisterUser(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser { UserName = model.Email, Email = model.Email, EmailConfirmed = false };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {

                    // Check to see what type of user this is and give the user the correct permissions
                    var permissionsResult = await UpdatePermissions(model.Email);
                    // if the permissions change did not succeed, note errors and return to login screen
                    if (!permissionsResult.Succeeded)
                    {
                        ModelState.AddModelError("", permissionsResult.Errors.First().ToString());
                        return View(model);
                    }
                    
                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

                    // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
                    // Send an email with this link
                    // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                    // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                    // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

                    return RedirectToAction("Index", "Home");
                }
                AddErrors(result);
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        [Authorize(Users = "tchase,jstocksdale,rfulks")]
        public ActionResult ImpersonateUser()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize(Users = "tchase,jstocksdale,rfulks,derothfus")]
        public async Task<ActionResult> ImpersonateUser(string commandtxt,string UserID,string NewPassword = "",string ConfirmPassword = "")
        {
            double parseresult;
            string LogonName = "";
            ApplicationUser userobj = null;
            DataTable ReturnedUser = null;

            //if(UserID == "" || !Double.TryParse(UserID, out parseresult))
            //{
            //    ViewBag.ResultMessage = "The User ID specified is not a number.";
            //    return PartialView();
            //}
            if(NewPassword != ConfirmPassword)
            {
                ViewBag.ResultMessage = "Passwords do not match.";
                return View();
            }
            if (commandtxt != "impersonateNEU")
            {
                Dictionary<string, string> Parameters = new Dictionary<string, string>();
                Parameters.Add("JSON", "{\"@P1\":\"" + UserID + "\"}");

                ReturnedUser = DataFuncs.ReadDataFromDB(0, "Security_Logon", Parameters);
                //ReturnedUser = DataFuncs.GetAnyTable("Security_Logon", "PersonID = " + UserID);
                if (ReturnedUser.Rows.Count == 0)
                {
                    ViewBag.ResultMessage = "User ID not found.";
                    return View();
                }
                LogonName = ReturnedUser.Rows[0]["Logon"].ToString();

                userobj = await UserManager.FindByNameAsync(LogonName);
                if (userobj == null)
                {
                    ViewBag.ResultMessage = "User ID not found.";
                    return View();
                }
            }
            switch (commandtxt)
            {
                case "impersonate":
                    var permissionsResult = await UpdatePermissions(LogonName);
                    // if the permissions change did not succeed, note errors and return to login screen
                    if (!permissionsResult.Succeeded)
                    {
                        ViewBag.ResultMessage = permissionsResult.Errors.First().ToString();
                        return View();
                    }

                    // automatically sign in the new user.
                    await SignInManager.SignInAsync(userobj, isPersistent: false, rememberBrowser: false);

                    return RedirectToAction("Index","Home");
                case "changepw":
                    string code = await UserManager.GeneratePasswordResetTokenAsync(userobj.Id);
                    var result = await UserManager.ResetPasswordAsync(userobj.Id, code, NewPassword);
                    if (result.Succeeded)
                    {
                        ViewBag.ResultMessage = "Password changed.";
                        return View();
                    }
                    else
                    {
                        ViewBag.ResultMessage = "Password did not change due to an error.";
                        return View();
                    }

                    break;
                case "impersonateNEU":
                    ReturnedUser = DataFuncs.GetAnyTable("Persons", "PersonID = " + UserID);
                    if (ReturnedUser.Rows.Count == 0)
                    {
                        ViewBag.ResultMessage = "User ID not found.";
                        return View();
                    }
                    Session["AuthUserFullName"] = ReturnedUser.Rows[0]["Fname"].ToString();
                    Session["AuthUserPersonID"] = UserID;
                    return RedirectToAction("Index", "Home");
                    break;
                default:
                    ViewBag.ResultMessage = "Invalid command request.";
                    return View();
            }
        }

        [Authorize(Users = "tchase,jstocksdale,dtilly")]
        public ActionResult NewParents()
        {
            DataTable ParentEmails = DataFuncs.ReadDataFromDB(Convert.ToInt32(BasicFuncs.GetPersonIDFromUsername(User.Identity.Name)),"NewParents");

            ViewBag.ResultMessage = "";
            ViewBag.NumAccounts = ParentEmails.Rows.Count;
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize(Users = "tchase,jstocksdale,dtilly")]
        public async Task<ActionResult> NewParents(string runthis)
        {
            string Subject;
            string MessageBody;
            Dictionary<string, string> PersonName;
            Dictionary<string, string> Parameters;
            string StudentID="";
            string AdmissionsID;
            // Get the Current UserID
            int RequestorID = Convert.ToInt32(BasicFuncs.GetPersonIDFromUsername(User.Identity.Name));

            ViewBag.UserID2 = User.Identity.GetEntityID();
            DataTable ParentEmails = DataFuncs.ReadDataFromDB(RequestorID,"NewParents");

            // Generate and send logins
            Subject = "New Parent Portal Login Information";
            MessageBody = "<p>Dear #Name#</p>" + Environment.NewLine
                + "<p>RVA has a secure web portal (information website) which can be reached at <a href=https://portal.rva.org>https://portal.rva.org</a></p>"
                + "<p>Your username is #Email#<br/>"
                + "and your password is #Password#</p>"
                + "<p>You will have to change your password the first time you log in.</p>"
                + "<p>You can access this portal and the TAB named PARENT INFORMATION to find the enrollment forms necessary to complete your child's admissions process.</p>"
                + "<p>The portal is still in development, so please email us at portalhelp@rva.org if you have problems or questions.</p>"
                + "<p>Thank you,<br/>"
                + "The RVA Portal Development Team</p>";
            ParentEmails.Columns.Add("Password");
            ParentEmails.Columns.Add("Name");
            ParentEmails.Columns["ParentID"].ColumnName = "EntityID";
            // The Logon column cannot be renamed email because there is already a column with that name.
            //ParentEmails.Columns["Logon"].ColumnName = "Email";

            foreach (DataRow TempRow in ParentEmails.Rows)
            {
                // Create the password for each entry
                TempRow["Password"] = BasicFuncs.CreatePassword(8);

                // Retrieve the parent's name from the database and save it in the "Name" column
                PersonName = new Dictionary<string,string>();
                PersonName.Add("Filter", "EntityID = " + TempRow["EntityID"].ToString());
                TempRow["Name"] = DataFuncs.ReadDataFromDB(Convert.ToInt32(BasicFuncs.GetPersonIDFromUsername(User.Identity.Name)), "Person",PersonName).Rows[0]["FullName"].ToString();
                
                // Create a relationship of Logon with Entity1ID of ParentID and Entity2ID of the EntityID of the Contact EmailID 
                string JsonOutput = "[{\"ShortRelationshipName\":\"Logon\",\"Entity1ID\":\"" + TempRow["EntityID"] + "\""
                                  + ",\"Entity2ID\":\"" + TempRow["ConEmailID"].ToString() + "\"}]";
                using (var client = new HttpClient())
                {
                    JObject postData = new JObject();
                    postData.Add("models", JsonOutput);
                    // Assuming the API is in the same web application. 
                    string baseUrl = new Uri(Request.Url.AbsoluteUri).GetLeftPart(UriPartial.Authority);
                    client.BaseAddress = new Uri(baseUrl);
                    JArray result = client.PutAsync("/api/Write/EntityRelationship", postData,
                                        new System.Net.Http.Formatting.JsonMediaTypeFormatter())
                                        .Result
                                        .Content
                                        .ReadAsAsync<JArray>()
                                        .Result;
                    if (result.ToString().Contains("Error"))
                    {
                        ViewBag.ResultMessage += "Error when creating logon entry for " + JsonOutput;
                        return View();
                    }
                }

                // register the user
                var user = new ApplicationUser { UserName = TempRow["Email"].ToString(), Email = TempRow["Email"].ToString(), EmailConfirmed = false };
                var result2 = await UserManager.CreateAsync(user, TempRow["Password"].ToString());
                //var result2 = await Register(new RegisterViewModel { Email = TempRow["Email"].ToString(), Password = TempRow["Password"].ToString() });
                if (!result2.Succeeded)
                {
                    ViewBag.ResultMessage += "The following error was experienced while creating users: " + result2.Errors.First().ToString()
                            + "<br />Please resolve the error for " + TempRow["EntityID"] + "|" + TempRow["Email"] + "|" + TempRow["Name"] + " and try again.";
                    // there was an error, so immediately stop processing.
                    return View();
                }

                // notify the user of the account creation
                //ViewBag.ResultMessage += BasicFuncs.SendEmail(Subject, MessageBody, (new DataRow[] { TempRow }).CopyToDataTable(),"",true);
                ViewBag.ResultMessage += "<br />" + TempRow["Email"] + " | ";
                ViewBag.ResultMessage += BasicFuncs.SendEmail(Subject, MessageBody, (new DataRow[] { TempRow }).CopyToDataTable(), "", false);
             //   StudentID = TempRow["StudentID"].ToString();
            
            }
            // Maybe there is no reason to do this step - it is related to the parent - NOT the student or the AdmissionsRequest.

            //// Flag this step in Admissions process as complete.
            //Parameters = new Dictionary<string,string>();
            //Parameters.Add("@P1",StudentID);
            //AdmissionsID=DataFuncs.ReadDataFromDB(RequestorID,"GetAdmissionsRequestID",Parameters).Rows[0]["AdmissionsID"].ToString();

            ////// Currently Hardcoded JSON to write to the Admissions Acceptance EntityID of 16700 - needs to be done in a better way.
            //string AdmCompJSON = "[{\"ShortRelationshipName\":\"ADMCOMP\",\"Entity1ID\":\"" + AdmissionsID + "\""
            //  + ",\"Entity2ID\":\"" + "16700" + "\"}]";
            
            //using (var client = new HttpClient())
            //{
            //    JObject postData = new JObject();
            //    postData.Add("models", AdmCompJSON);
            //    // Assuming the API is in the same web application. 
            //    string baseUrl = new Uri(Request.Url.AbsoluteUri).GetLeftPart(UriPartial.Authority);
            //    client.BaseAddress = new Uri(baseUrl);
            //    JArray result = client.PutAsync("/api/sqlsp/EntityRelationship", postData,
            //                        new System.Net.Http.Formatting.JsonMediaTypeFormatter())
            //                        .Result
            //                        .Content
            //                        .ReadAsAsync<JArray>()
            //                        .Result;
            //    if (result.ToString().Contains("Error"))
            //    {
            //        ViewBag.ResultMessage = "Error when creating completion relationship for " + AdmCompJSON;
            //        return View();
            //    }
            //}

            ViewBag.NumAccounts = ParentEmails.Rows.Count;
            return View();
        }

        //
        // POST: /Account/Register
        [Authorize(Users = "tchase")]
        public async Task<IdentityResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser { UserName = model.Email, Email = model.Email, EmailConfirmed = false };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {

                    // Check to see what type of user this is and give the user the correct permissions
                    var permissionsResult = await UpdatePermissions(model.Email);
                    // if the permissions change did not succeed, note errors and forward to error page
                    if (!permissionsResult.Succeeded)
                    {
                        return permissionsResult;
                    }

                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

                    // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
                    // Send an email with this link
                    // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                    // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                    // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

                    return IdentityResult.Success;
                }
                return result;
            }
            // If we got this far, something failed, return error
            return new IdentityResult(new string[] { "The information passed is not valid." });
        }

        ////
        //// POST: /Account/RegisterMulti
        //[HttpPost]
        //[Authorize(Users="tchase")]
        //[ValidateAntiForgeryToken]
        //public async Task<ActionResult> RegisterMulti(DataTable UserTable)
        //{
        //    if(!UserTable.Columns.Contains("EntityID") || !UserTable.Columns.Contains("Logon") || !UserTable.Columns.Contains("Password"))
        //    {
        //        ViewBag.OtherError = "Table of users missing one of the following required columns: EntityID, Logon, Password <br /> Please try again.";
        //        return View("Error");
        //    }
        //    foreach(DataRow TempRow in UserTable.Rows)
        //    {
        //        if(!BasicFuncs.IsValidEmail(TempRow["Logon"].ToString()))
        //        {
        //            ViewBag.OtherError = "The user with an ID of " + TempRow["EntityID"].ToString() + " does not have a proper email address as his/her logon.  Instead it is " 
        //                + TempRow["Logon"] + ". <br /> Please Resolve and try again.";
        //            return View("Error");
        //        }
        //        var user = new ApplicationUser { UserName = TempRow["Logon"].ToString(), Email = TempRow["Logon"].ToString(), EmailConfirmed = true };
        //        var result = await UserManager.CreateAsync(user, TempRow["Password"].ToString());
        //        if (result.Succeeded)
        //        {

        //            // Check to see what type of user this is and give the user the correct permissions
        //            var permissionsResult = await UpdatePermissions(TempRow["Logon"].ToString());
        //            // if the permissions change did not succeed, note errors and forward to error page
        //            if (!permissionsResult.Succeeded)
        //            {
        //                ViewBag.OtherError = permissionsResult.Errors.First().ToString();
        //                return View("Error");
        //            }

        //            await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

        //            //return RedirectToAction("Index", "Home");
        //        }
        //        else 
        //        {
        //            ViewBag.OtherError = result.Errors.First().ToString();
        //            return View("Error");
        //        }
        //    }
        //    ViewBag.OtherError = "Accounts created successfully";
        //    return View("Error");
        //}

        //
        // GET: /Account/ConfirmEmail
        [AllowAnonymous]
        public async Task<ActionResult> ConfirmEmail(string userId, string code)
        {
            if (userId == null || code == null)
            {
                return View("Error");
            }
            var result = await UserManager.ConfirmEmailAsync(userId, code);
            return View(result.Succeeded ? "ConfirmEmail" : "Error");
        }
        #endregion

        #region Password
        //
        // GET: /Account/ForgotPassword
        [AllowAnonymous]
        public ActionResult ForgotPassword()
        {
            return View();
        }

        //
        // POST: /Account/ForgotPassword
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindByNameAsync(model.Email);
                if (user == null)
                {
                    // Don't reveal that the user does not exist
                    return View("ForgotPasswordConfirmation");
                }
                if (!(await UserManager.IsEmailConfirmedAsync(user.Id)))
                {
                    return View("ForgotPasswordNoEmailConfirmed");
                }

                // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
                // Send an email with this link
                string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
                var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                await UserManager.SendEmailAsync(user.Id, "RVA Password Change Request", "Please click on the following link to change your RVA Secure Portal Password: <a href=\"" + callbackUrl + "\">Change Password</a>");
                return RedirectToAction("ForgotPasswordConfirmation", "Account");
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // GET: /Account/ForgotPasswordConfirmation
        [AllowAnonymous]
        public ActionResult ForgotPasswordConfirmation()
        {
            return View();
        }

        //
        // GET: /Account/ForgotPasswordNoEmailConfirmed
        [AllowAnonymous]
        public ActionResult ForgotPasswordNoEmailConfirmed()
        {
            return View();
        }

        //
        // GET: /Account/ResetPassword
        [AllowAnonymous]
        public ActionResult ResetPassword(string code)
        {
            return code == null ? View("Error") : View();
        }

        //
        // POST: /Account/ResetPassword
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            var user = await UserManager.FindByNameAsync(model.Email);
            if (user == null)
            {
                // Don't reveal that the user does not exist
                return RedirectToAction("ResetPasswordConfirmation", "Account");
            }
            var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction("ResetPasswordConfirmation", "Account");
            }
            AddErrors(result);
            return View();
        }

        //
        // GET: /Account/ResetPasswordConfirmation
        [AllowAnonymous]
        public ActionResult ResetPasswordConfirmation()
        {
            return View();
        }
        #endregion

        #region ExternalLogin
        //
        // POST: /Account/ExternalLogin
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            // Request a redirect to the external login provider
            return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
        }

        //
        // GET: /Account/ExternalLogin
        [AllowAnonymous]
        public ActionResult ExternalLogin(string returnUrl)
        {
            // Request a redirect to the external login provider
            return new ChallengeResult("Windows", Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
        }

        //
        // GET: /Account/ExternalLoginCallback
        [AllowAnonymous]
        public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
            if (loginInfo == null)
            {
                return RedirectToAction("Login");
            }

            // Attempt to update the user's permissions.  If this user does not exist, it will be created farther down
            // and this function will be run again.
            var permissionsResultFirst = await UpdatePermissions("", loginInfo);
            if (!permissionsResultFirst.Succeeded)
            {
                ViewBag.OtherError = permissionsResultFirst.Errors.First().ToString();
                // Invalid Login Attempt means the user does not exist.
                if (ViewBag.OtherError != "Invalid login attempt.")
                {
                    return View("Error");
                }
            }


            // Sign in the user with this external login provider if the user already has a login
            var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
            switch (result)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
                case SignInStatus.Failure:
                default:
                    //// If the user does not have an account, then prompt the user to create an account
                    //ViewBag.ReturnUrl = returnUrl;
                    //ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
                    //return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });

                    // create the user based on windows sign-in information.
                    var user = new ApplicationUser { UserName = loginInfo.DefaultUserName.Split('\\')[1], Email = loginInfo.DefaultUserName.Split('\\')[1] + "@rva.org" };
                    var result2 = await UserManager.CreateAsync(user);
                    if (result2.Succeeded)
                    {
                        result2 = await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
                        if (result2.Succeeded)
                        {

                            var permissionsResult = await UpdatePermissions("", loginInfo);
                            // if the permissions change did not succeed, note errors and forward to error page
                            if (!permissionsResult.Succeeded)
                            {
                                ViewBag.OtherError = permissionsResult.Errors.First().ToString();
                                return View("Error");
                            }

                            await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

                            return RedirectToLocal(returnUrl);
                        }
                    }
                    return RedirectToAction("Login");
            }
        }

        //
        // POST: /Account/ExternalLoginConfirmation
        // Currently Not In Use
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
        {
            if (User.Identity.IsAuthenticated)
            {
                return RedirectToAction("Index", "Manage");
            }

            if (ModelState.IsValid)
            {
                // Get the information about the user from the external login provider
                var info = await AuthenticationManager.GetExternalLoginInfoAsync();
                if (info == null)
                {
                    return View("ExternalLoginFailure");
                }
                var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
                var result = await UserManager.CreateAsync(user);
                if (result.Succeeded)
                {
                    result = await UserManager.AddLoginAsync(user.Id, info.Login);
                    if (result.Succeeded)
                    {
                        await UpdatePermissions("", info);

                        await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

                        return RedirectToLocal(returnUrl);
                    }
                }
                AddErrors(result);
            }

            ViewBag.ReturnUrl = returnUrl;
            return View(model);
        }

        //
        // GET: /Account/ExternalLoginFailure
        [AllowAnonymous]
        public ActionResult ExternalLoginFailure()
        {
            return View();
        }
        #endregion

        #region TwoFactorAuth
        //
        // GET: /Account/SendCode
        [AllowAnonymous]
        public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe)
        {
            var userId = await SignInManager.GetVerifiedUserIdAsync();
            if (userId == null)
            {
                return View("Error");
            }
            var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
            var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
            return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
        }

        //
        // POST: /Account/SendCode
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> SendCode(SendCodeViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View();
            }

            // Generate the token and send it
            if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
            {
                return View("Error");
            }
            return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
        }
        #endregion

        //
        // POST: /Account/LogOff
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff()
        {
            AuthenticationManager.SignOut();
            return RedirectToAction("LoginNR", "Account");
        }

        /// <summary>
        /// Removes all permissions and adds the currently logged-in user's permissions based on a database request.  
        /// MUST BE RUN BEFORE THE SIGNIN COMMAND!
        /// </summary>
        /// <param name="UserName">The name of the user whose permissions will be managed.</param>
        /// <param name="WinLogin">If the user is a windows user, this should contain the windows authentication object.</param>
        /// <returns></returns>
        private async Task<IdentityResult> UpdatePermissions(string UserName, ExternalLoginInfo WinLogin = null)
        {
            DataTable WindowsPermissions;
            DataTable UserPermissions;
            DataTable WindowsGroups;
            DataTable UserInfo;
            DataRow TempInfo;
            string LogonName;
            ApplicationUser userobj;
            IdentityResult modifyResult;
            System.Collections.ArrayList UsersADGroups = new ArrayList();
            string GroupName;

            if (WinLogin == null)
            {
                // since the main password box also supports domain authentication, 
                // this needs to check for the domain\\ and remove it if found.
                if (UserName.IndexOf("\\") != -1)
                {
                    LogonName = UserName.Split('\\')[1];
                }
                else
                {
                    LogonName = UserName;
                }
            }
            else
            {
                // Split the domain off the name and use what is left as the user name for the request
                LogonName = WinLogin.DefaultUserName.Split('\\')[1];
            }

            // Find the user object
            userobj = await UserManager.FindByNameAsync(LogonName);

            // This checks if the user exists.  All windows users will hit this first, then be in the database
            // for the second run.  
            if (userobj == null)
            {
                return new IdentityResult(new string[] { "Invalid login attempt." });
            }

            // if the username does not contain an @ symbol, it might be a domain user
            if (LogonName.IndexOf('@') == -1)
            {
                ADFuncs MyADQuery = new ADFuncs();

                if (MyADQuery.IsUserExisiting(@"RVA\" + LogonName))
                {
                    UsersADGroups = MyADQuery.GetUserGroups(@"RVA\" + LogonName);
                }
            }


            //// Load the list of valid asp.net roles - here usually called permissions - to check for the existence of the requested role.
            //RolesController roleChecker = new RolesController();
            //List<IdentityRole> permissionList = await roleChecker.ListAllRoles();

            //// Remove all permissions of the requested user in order to ensure any permissions changes are recorded.
            //var rolesForUser = UserManager.GetRoles(userobj.Id);
            //if (rolesForUser.Count() > 0)
            //{
            //    foreach (var item in rolesForUser)
            //    {
            //        var result = UserManager.RemoveFromRole(userobj.Id, item);
            //    }
            //}



            // This gets the User's name and associated personids based on the view SECURITY_GETUSERINFO
            // and stores the info in a cookie for later use
            //UserInfo = DataFuncs.GetAnyTable("SECURITY_GETUSERINFO", "Logon = '" + userobj.UserName + "'");
            Dictionary<string,string> Parameters = new Dictionary<string,string>();
            Parameters.Add("JSON", "{\"@P1\":\"" + userobj.UserName + "\"}");
            
            
            
            UserInfo = DataFuncs.ReadDataFromDB(0,"Security_Logon",Parameters);
            // In case there is no database entry, make the visible name the username and the userid the userid.
            if (UserInfo.Rows.Count == 0 || UserInfo.Columns[0].ColumnName == "Error")
            {
                Session["AuthUserFullName"] = userobj.UserName;
                Session["AuthUserPersonID"] = userobj.Id;
                return IdentityResult.Success;
            }
            else
            {
                TempInfo = UserInfo.Rows[0];
                //HttpCookie UserInfoCookie = new HttpCookie("UserInfo");
                //UserInfoCookie["AuthUserFullName"] = TempInfo["Name"].ToString();
                //UserInfoCookie["AuthUserPersonID"] = TempInfo["PersonID"].ToString();
                Session["AuthUserFullName"] = TempInfo["FullName"].ToString();
                Session["AuthUserPersonID"] = TempInfo["EntityID"].ToString();

            }
            var result = await UserManager.AddClaimAsync(userobj.Id, new Claim("EntityID", TempInfo["EntityID"].ToString()));
            if (!result.Succeeded)
            {
                return result;
            }


            //userobj.Claims.Add(new Claim("RafikiSIS_Site:EntityID", TempInfo["EntityID"].ToString()));
            //if(System.Configuration.ConfigurationManager.AppSettings["DebuggingUser"] != "")
            //{
            //    Session["AuthUserPersonID"] = System.Configuration.ConfigurationManager.AppSettings["DebuggingUser"];
            //}

            //Response.Cookies.Add(UserInfoCookie);

            // WINAPI Access Cookies:
            // Cookie with the stored user id
            //string sessionId = "";
            //CookieHeaderValue cookie = Request.Headers.GetCookies("session-id").FirstOrDefault();
            //if (cookie != null)
            //{
            //    sessionId = cookie["session-id"].Value;
            //}


            // If the function reaches this point, it was successful.
            return IdentityResult.Success;
        }

        #region Helpers
        // Used for XSRF protection when adding external logins
        private const string XsrfKey = "XsrfId";

        private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
        }

        private void AddErrors(IdentityResult result)
        {
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError("", error);
            }
        }

        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            return RedirectToAction("Index", "Home");
        }

        internal class ChallengeResult : HttpUnauthorizedResult
        {
            public ChallengeResult(string provider, string redirectUri)
                : this(provider, redirectUri, null)
            {
            }

            public ChallengeResult(string provider, string redirectUri, string userId)
            {
                LoginProvider = provider;
                RedirectUri = redirectUri;
                UserId = userId;
            }

            public string LoginProvider { get; set; }
            public string RedirectUri { get; set; }
            public string UserId { get; set; }

            public override void ExecuteResult(ControllerContext context)
            {
                var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
                if (UserId != null)
                {
                    properties.Dictionary[XsrfKey] = UserId;
                }
                context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
            }
        }
        #endregion
    }
}